#include <stdio.h>
#include <string.h>
#include <unistd.h> // Unix standard functions
#include <fcntl.h>  // File control
#include <errno.h>  // Error number
#include <termios.h>// POSIX terminal control


/* Initialize serial port
 * Setup baud rate and various flags
 */
int initPort(char *device) {
	int fd = open(device, O_RDWR | O_NOCTTY);
	if (fd == -1) {
		perror("FATAL: Failed to open serial port");
		return -1;
	} else {
		fcntl(fd, F_SETFL, 0);
	}

    struct termios options;

    // Get current port options
    tcgetattr(fd, &options);

    // Set rate to 19200bps
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);

    // Enable the receiver and set local mode. 
    // Must always enable.
    options.c_cflag |= (CLOCAL | CREAD);

    // Set parity to 8N1
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

    // Set serial port options
    options.c_iflag |= IGNBRK;
    options.c_iflag &= ~BRKINT;
    options.c_iflag &= ~ICRNL;
    options.c_iflag &= ~IMAXBEL;

    options.c_oflag &= ~OPOST;
    options.c_oflag &= ~ONLCR;

    options.c_lflag &= ~ISIG;
    options.c_lflag &= ~ICANON;
    options.c_lflag &= ~IEXTEN;
    options.c_lflag &= ~ECHO;
    options.c_lflag &= ~ECHOE;
    options.c_lflag &= ~ECHOK;
    options.c_lflag &= ~ECHOCTL;
    options.c_lflag &= ~ECHOKE;

    // Apply the changes above to the port
    tcsetattr(fd, TCSANOW, &options);

    return fd;
}

/* Read from serial port
 *  fd: file descriptor of serial port
 *  result: pointer holding to read result
 *  bytesRequested: number of bytes to read
 * Returns number of byte read, -1 on error
 */
int readPort(int fd, char* result, int bytesRequested) {
    int totalBytesRead = 0,
    	bytesRead = 0;

    while (totalBytesRead < bytesRequested) {
       if (bytesRead < 0) {    // If error occurred
           printf("FATAL: read() error %d %s\n", errno, strerror(errno));
           return -1;
       } else {    // If number of bytes read is less than requested
           bytesRead = read(fd, result+totalBytesRead, bytesRequested-totalBytesRead);
           totalBytesRead+=bytesRead;
       }
    }
    return totalBytesRead;
}

/* FIXME: Very server! Potential buffer over flow
 * Write to serial port
 *  fd: file descritpor of serial port
 *  chars: pointer to characters to be written
 * Returns number of byte written
 */
int writePort(int fd, char* chars) {
    int len = strlen(chars);
    chars[len] = 0x0d;  // <CR>
    chars[len+1] = 0x00; // \0 to terminate the string
    // Write to fd
    int n = write(fd, chars, strlen(chars));
    // Handle error
    if (n < 0) fputs("write() failed.\n", stderr);
    return n;
}

/* Returns Baud rate of serial port */
int getBaudRate(int fd) {
    struct termios termAttr;
    int inputSpeed = -1;
    speed_t baudRate;
    tcgetattr(fd, &termAttr);
    // Get input speed
    baudRate = cfgetispeed(&termAttr);
    switch (baudRate) {
        case B0:      inputSpeed = 0; break;
        case B50:     inputSpeed = 50; break;
        case B110:    inputSpeed = 110; break;
        case B134:    inputSpeed = 134; break;
        case B150:    inputSpeed = 150; break;
        case B200:    inputSpeed = 200; break;
        case B300:    inputSpeed = 300; break;
        case B600:    inputSpeed = 600; break;
        case B1200:   inputSpeed = 1200; break;
        case B1800:   inputSpeed = 1800; break;
        case B2400:   inputSpeed = 2400; break;
        case B4800:   inputSpeed = 4800; break;
        case B9600:   inputSpeed = 9600; break;
        case B19200:  inputSpeed = 19200; break;
        case B38400:  inputSpeed = 38400; break;
    }
    return inputSpeed;
}

